/**
 *
 */
package hichecker.ui.custom_views.series;

import hichecker.controller.Config;
import hichecker.ui.components.custom_table.AbstractCustomTableModel;
import hichecker.ui.components.custom_table.column_class.Grade;
import hichecker.ui.components.custom_table.column_class.Points;
import hichecker.university.PTLSeries;
import hichecker.university.Series;
import hichecker.university.Student;
import hichecker.university.Task;

import java.util.ArrayList;
import java.util.List;

/**
 *
 */
public class SeriesViewTableModel extends AbstractCustomTableModel<String> {

	protected Series series;
	protected List<Student> students;
	protected List<Column> dynamicColumns;
	protected enum Column {TASK, SUM, GRADE}

	protected int FIXED_COLUMNS = 4;

	private List<Integer> grouping;

	/**
	 * Erzeugt ein neues Model. Benötigt dafür eine Serie, mit dem es gefüllt
	 * wird, sowie eine Liste aller Studenten, die im Kurs teilnehmen
	 *
	 * @param students
	 * @param series
	 */
	public SeriesViewTableModel(List<Student> students, Series series) {
		this.series = series;
		this.students = students;
		grouping = new ArrayList<Integer>();

		// Dynamische Spalten erzeugen
		dynamicColumns = new ArrayList<Column>();

		// Jede Aufgabe eine Spalte
		for (int i = 0; i < series.getNumTasks(); i++)
			dynamicColumns.add(Column.TASK);

		// If there's one task add sum and grade column
		if (series.getNumTasks() > 0) {
			// Summenspalte
			dynamicColumns.add(Column.SUM);
			// Note, falls es sich um eine PTL handelt
			if (series instanceof PTLSeries)
				dynamicColumns.add(Column.GRADE);
		}
	}

	@Override
	protected String getRowID(int row) {
		if (row >= students.size()) return null;
		return "" + students.get(row).getID();
	}

	@Override
	public Object getValueAt(int row, int column) {
		// Letzt Zeile => alles leer
		if (row == getRowCount() - 1) return null;
		// Bereichsüberschreitung
		if (row >= getRowCount() || column >= getColumnCount()) return null;

		Student curStudent = students.get(row);
		// Standardspalte
		switch(column) {
		case 0: return curStudent.getLastName();
		case 1: return curStudent.getFirstName();
		case 2: return curStudent.getMatrikel();
		case 3: return curStudent.getStudyCourse();
		}

		// Dynamische Spalten durchgehen
		column -= FIXED_COLUMNS;
		if (column >= dynamicColumns.size()) return null;

		switch (dynamicColumns.get(column)) {
		case TASK:
			float check = series.getGainedPoints(curStudent, column);
			return check < 0 ? null : check;
		case SUM:
			check = series.getSumGainedPoints(curStudent);
			return check < 0 ? null : check;
		case GRADE:
			check = ((PTLSeries) series).getGrade(curStudent);
			return check == -1 ? null : check;
		default: return null;
		}
	}

	@Override
	public void setValueAt(Object value, int row, int column) {
		// Bereichsüberschreitung
		if (row >= getRowCount() || column >= getColumnCount()) return;

		// Check if were at the last line. It's the case if all values are null
		boolean lastLine = row >= students.size();

		Student curStudent;
		if (lastLine) {
			addRow();
		}

		curStudent = students.get(row);

		switch (column) {
		case 0:
			curStudent.setLastName("" + value);
			return;
		case 1:
			curStudent.setFirstName("" + value);
			return;
		case 2:
			curStudent.setMatrikel("" + value);
			return;
		case 3:
			curStudent.setStudyCourse("" + value);
			return;
		default: column -= FIXED_COLUMNS;
		}

		switch (dynamicColumns.get(column)) {
		case TASK:
			float gainedPoints = Float.parseFloat("" + value);

			// If the student is part of the current grouping add points to all members
			if (grouping.contains(row))
				for (Integer index : grouping)
					series.setGainedPoints(students.get(index), gainedPoints, column);
			else
				// Just the one student
				series.setGainedPoints(curStudent, gainedPoints, column);
			break;
		case GRADE:
			float grade = Float.parseFloat("" + value);

			// If the student is part of the current grouping add grade to all members
			if (grouping.contains(row))
				for (Integer index : grouping)
					((PTLSeries) series).setGrade(students.get(index), grade);
			// Just the one student
			else
				((PTLSeries) series).setGrade(curStudent, grade);
			break;
		default: return;
		}
		fireTableDataChanged();
	}

	@Override
	public int getColumnCount() {
		return FIXED_COLUMNS + dynamicColumns.size();
	}

	@Override
	public int getRowCount() {
		return students.size() + 1;
	}

	@Override
	public boolean isCellEditable(int row, int column) {
		// Standardspalten sind editierbar
		if (column < FIXED_COLUMNS) return true;

		// Andere Spalten bis auf Summenspalte editierbar
		column -= FIXED_COLUMNS;
		return dynamicColumns.size() > column && dynamicColumns.get(column) == Column.SUM
		? false : true;
	}

	public void addRow() {
		students.add(new Student());
		fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
	}

	public void deleteRow(int row) {
		if (row < students.size()) {
			students.remove(row);
			fireTableRowsInserted(row, row);
		}
	}

	public void setGrouping(int[] selectedRows) {
		grouping.clear();

		// Add all rows except of the last one
		for (int i : selectedRows)
			if (i != getRowCount() - 1) grouping.add(i);
		fireTableDataChanged();
	}

	public void clearGrouping() {
		grouping.clear();
		fireTableDataChanged();
	}

	/**
	 * Indicates whether a specific row is part of the current grouping
	 * @param row
	 * @return
	 */
	public boolean isInGroup(int row) {
		return grouping.contains(row);
	}

	@Override
	public Class<?> getColumnClass(int column) {
		// Standardspalte
		if (column < FIXED_COLUMNS)
			return column == 2 ? Integer.class : Object.class;

		column -= FIXED_COLUMNS;
		if (column >= dynamicColumns.size()) return Object.class;

		switch (dynamicColumns.get(column)) {
		case TASK: return Points.class;
		case GRADE: return Grade.class;
		case SUM: return Integer.class;
		default: return Object.class;
		}
	}

	@Override
	public Object[] getHeaderValues() {
		int colCount = getColumnCount();
		headerValues = new Object[colCount];

		// The student data field

		headerValues[0] = "<html><br>" + Config.STUDENT_LAST_NAME + "</html>";
		headerValues[1] = "<html><br>" + Config.STUDENT_FIRST_NAME + "</html>";
		headerValues[2] = "<html><br>" + Config.STUDENT_MATRIKEL + "</html>";
		headerValues[3] = "<html><br>" + Config.STUDENT_STUDY_COURSE + "</html>";

		// Add each tasks
		for (int i = 0; i < series.getNumTasks(); i++) {
			Task task = series.getTaskAt(i);
			headerValues[i + 4] = "<html> " + task.getName() + (task.isOptional() ? "*" : "")
			+ "<br>" + task.getMaxPoints() + "</htm>";
		}

		// There's at least one series
		if (series.getNumTasks() > 0) {
			int sumIndex = series instanceof PTLSeries ? colCount - 2 : colCount - 1;
			headerValues[sumIndex] = "<html>∑<br>" + series.getSumMax() + "</html>";
			if (series instanceof PTLSeries) headerValues[sumIndex + 1] = Config.SERIES_GRADE;
		}

		return headerValues;
	}

}
